home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / vidhrdw / twincobr.c < prev    next >
C/C++ Source or Header  |  2000-05-04  |  20KB  |  677 lines

  1. /***************************************************************************
  2.   vidhrdw.c
  3.  
  4.   Functions to emulate the video hardware of these machines.
  5.   Video is 30x40 tiles. (200x320 for Twin Cobra/Flying shark)
  6.   Video is 40x30 tiles. (320x200 for Wardner)
  7.  
  8.   Video has 3 scrolling tile layers (Background, Foreground and Text) and
  9.   Sprites that have 4 (5?) priorities. Lowest priority is "Off".
  10.   Wardner has an unusual sprite priority in the shop scenes, whereby a
  11.   middle level priority Sprite appears over a high priority Sprite ?
  12.  
  13. ***************************************************************************/
  14.  
  15. #include "driver.h"
  16. #include "vidhrdw/generic.h"
  17. #include "vidhrdw/crtc6845.h"
  18.  
  19.  
  20. static unsigned char *twincobr_bgvideoram;
  21. static unsigned char *twincobr_fgvideoram;
  22.  
  23. int wardner_sprite_hack = 0;    /* Required for weird sprite priority in wardner  */
  24.                                 /* when hero is in shop. Hero should cover shop owner */
  25.  
  26. extern int toaplan_main_cpu;    /* Main CPU type.  0 = 68000, 1 = Z80 */
  27.  
  28. #define READ_WORD_Z80(x) (*(unsigned char *)(x) + (*(unsigned char *)(x+1) << 8))
  29. #define WRITE_WORD_Z80(a, d) (*(unsigned char *)(a) = d & 0xff, (*(unsigned char *)(a+1) = (d>>8) & 0xff))
  30.  
  31. static size_t twincobr_bgvideoram_size,twincobr_fgvideoram_size;
  32. static int txscrollx = 0;
  33. static int txscrolly = 0;
  34. static int fgscrollx = 0;
  35. static int fgscrolly = 0;
  36. static int bgscrollx = 0;
  37. static int bgscrolly = 0;
  38. int twincobr_fg_rom_bank = 0;
  39. int twincobr_bg_ram_bank = 0;
  40. int twincobr_display_on = 1;
  41. int twincobr_flip_screen = 0;
  42. int twincobr_flip_x_base = 0x37;    /* value to 0 the X scroll offsets (non-flip) */
  43. int twincobr_flip_y_base = 0x1e;    /* value to 0 the Y scroll offsets (non-flip) */
  44.  
  45. static int txoffs = 0;
  46. static int bgoffs = 0;
  47. static int fgoffs = 0;
  48. static int scroll_x = 0;
  49. static int scroll_y = 0;
  50.  
  51. static int vidbaseaddr = 0;
  52. static int scroll_realign_x = 0;
  53.  
  54. /************************* Wardner variables *******************************/
  55.  
  56. static int tx_offset_lsb = 0;
  57. static int tx_offset_msb = 0;
  58. static int bg_offset_lsb = 0;
  59. static int bg_offset_msb = 0;
  60. static int fg_offset_lsb = 0;
  61. static int fg_offset_msb = 0;
  62. static int tx_scrollx_lsb = 0;
  63. static int tx_scrollx_msb = 0;
  64. static int tx_scrolly_lsb = 0;
  65. static int tx_scrolly_msb = 0;
  66. static int bg_scrollx_lsb = 0;
  67. static int bg_scrollx_msb = 0;
  68. static int bg_scrolly_lsb = 0;
  69. static int bg_scrolly_msb = 0;
  70. static int fg_scrollx_lsb = 0;
  71. static int fg_scrollx_msb = 0;
  72. static int fg_scrolly_lsb = 0;
  73. static int fg_scrolly_msb = 0;
  74.  
  75.  
  76.  
  77. int twincobr_vh_start(void)
  78. {
  79.     /* the video RAM is accessed via ports, it's not memory mapped */
  80.     videoram_size = 0x1000;
  81.     twincobr_bgvideoram_size = 0x4000;    /* banked two times 0x2000 */
  82.     twincobr_fgvideoram_size = 0x2000;
  83.  
  84.     if ((videoram = malloc(videoram_size)) == 0)
  85.         return 1;
  86.     memset(videoram,0,videoram_size);
  87.  
  88.     if ((twincobr_fgvideoram = malloc(twincobr_fgvideoram_size)) == 0)
  89.     {
  90.         free(videoram);
  91.         return 1;
  92.     }
  93.     memset(twincobr_fgvideoram,0,twincobr_fgvideoram_size);
  94.  
  95.     if ((twincobr_bgvideoram = malloc(twincobr_bgvideoram_size)) == 0)
  96.     {
  97.         free(twincobr_fgvideoram);
  98.         free(videoram);
  99.         return 1;
  100.     }
  101.     memset(twincobr_bgvideoram,0,twincobr_bgvideoram_size);
  102.  
  103.     if ((dirtybuffer = malloc(twincobr_bgvideoram_size)) == 0)
  104.     {
  105.         free(twincobr_bgvideoram);
  106.         free(twincobr_fgvideoram);
  107.         free(videoram);
  108.         return 1;
  109.     }
  110.     memset(dirtybuffer,1,twincobr_bgvideoram_size);
  111.  
  112.     if ((tmpbitmap = osd_create_bitmap(Machine->drv->screen_width,2*Machine->drv->screen_height)) == 0)
  113.     {
  114.         free(dirtybuffer);
  115.         free(twincobr_bgvideoram);
  116.         free(twincobr_fgvideoram);
  117.         free(videoram);
  118.         return 1;
  119.     }
  120.  
  121.     return 0;
  122. }
  123.  
  124. void twincobr_vh_stop(void)
  125. {
  126.     osd_free_bitmap(tmpbitmap);
  127.     free(dirtybuffer);
  128.     free(twincobr_bgvideoram);
  129.     free(twincobr_fgvideoram);
  130.     free(videoram);
  131. }
  132.  
  133.  
  134. READ_HANDLER( twincobr_crtc_r )
  135. {
  136.     return crtc6845_register_r(offset);
  137. }
  138.  
  139. WRITE_HANDLER( twincobr_crtc_w )
  140. {
  141.     if (offset == 0) crtc6845_address_w(offset, data);
  142.     if (offset == 2) crtc6845_register_w(offset, data);
  143. }
  144.  
  145. int twincobr_txoffs_r(void)
  146. {
  147.     return txoffs / 2;
  148. }
  149. WRITE_HANDLER( twincobr_txoffs_w )
  150. {
  151.     txoffs = (2 * data) % videoram_size;
  152. }
  153. READ_HANDLER( twincobr_txram_r )
  154. {
  155.     return READ_WORD(&videoram[txoffs]);
  156. }
  157. WRITE_HANDLER( twincobr_txram_w )
  158. {
  159.     WRITE_WORD(&videoram[txoffs],data);
  160. }
  161.  
  162. WRITE_HANDLER( twincobr_bgoffs_w )
  163. {
  164.     bgoffs = (2 * data) % (twincobr_bgvideoram_size >> 1);
  165. }
  166. READ_HANDLER( twincobr_bgram_r )
  167. {
  168.     return READ_WORD(&twincobr_bgvideoram[bgoffs+twincobr_bg_ram_bank]);
  169. }
  170. WRITE_HANDLER( twincobr_bgram_w )
  171. {
  172.     WRITE_WORD(&twincobr_bgvideoram[bgoffs+twincobr_bg_ram_bank],data);
  173.     dirtybuffer[bgoffs / 2] = 1;
  174. }
  175.  
  176. WRITE_HANDLER( twincobr_fgoffs_w )
  177. {
  178.     fgoffs = (2 * data) % twincobr_fgvideoram_size;
  179. }
  180. READ_HANDLER( twincobr_fgram_r )
  181. {
  182.     return READ_WORD(&twincobr_fgvideoram[fgoffs]);
  183. }
  184. WRITE_HANDLER( twincobr_fgram_w )
  185. {
  186.     WRITE_WORD(&twincobr_fgvideoram[fgoffs],data);
  187. }
  188.  
  189.  
  190. WRITE_HANDLER( twincobr_txscroll_w )
  191. {
  192.     if (offset == 0) txscrollx = data;
  193.     else txscrolly = data;
  194. }
  195.  
  196. WRITE_HANDLER( twincobr_bgscroll_w )
  197. {
  198.     if (offset == 0) bgscrollx = data;
  199.     else bgscrolly = data;
  200. }
  201.  
  202. WRITE_HANDLER( twincobr_fgscroll_w )
  203. {
  204.     if (offset == 0) fgscrollx = data;
  205.     else fgscrolly = data;
  206. }
  207.  
  208. WRITE_HANDLER( twincobr_exscroll_w )    /* Extra unused video layer */
  209. {
  210.     if (offset == 0) logerror("PC - write %04x to extra video layer Y scroll register\n",data);
  211.     else logerror("PC - write %04x to extra video layer scroll X register\n",data);
  212. }
  213.  
  214. /******************** Wardner interface to this hardware ********************/
  215. WRITE_HANDLER( wardner_txlayer_w )
  216. {
  217.     if (offset == 0) tx_offset_lsb = data;
  218.     if (offset == 1) tx_offset_msb = (data << 8);
  219.     twincobr_txoffs_w(0,tx_offset_msb | tx_offset_lsb);
  220. }
  221. WRITE_HANDLER( wardner_bglayer_w )
  222. {
  223.     if (offset == 0) bg_offset_lsb = data;
  224.     if (offset == 1) bg_offset_msb = (data<<8);
  225.     twincobr_bgoffs_w(0,bg_offset_msb | bg_offset_lsb);
  226. }
  227. WRITE_HANDLER( wardner_fglayer_w )
  228. {
  229.     if (offset == 0) fg_offset_lsb = data;
  230.     if (offset == 1) fg_offset_msb = (data<<8);
  231.     twincobr_fgoffs_w(0,fg_offset_msb | fg_offset_lsb);
  232. }
  233.  
  234. WRITE_HANDLER( wardner_txscroll_w )
  235. {
  236.     if (offset & 2) {
  237.         if (offset == 2) tx_scrollx_lsb = data;
  238.         if (offset == 3) tx_scrollx_msb = (data<<8);
  239.         twincobr_txscroll_w(2,tx_scrollx_msb | tx_scrollx_lsb);
  240.     }
  241.     else
  242.     {
  243.         if (offset == 0) tx_scrolly_lsb = data;
  244.         if (offset == 1) tx_scrolly_msb = (data<<8);
  245.         twincobr_txscroll_w(0,tx_scrolly_msb | tx_scrolly_lsb);
  246.     }
  247. }
  248. WRITE_HANDLER( wardner_bgscroll_w )
  249. {
  250.     if (offset & 2) {
  251.         if (offset == 2) bg_scrollx_lsb = data;
  252.         if (offset == 3) bg_scrollx_msb = (data<<8);
  253.         twincobr_bgscroll_w(2,bg_scrollx_msb | bg_scrollx_lsb);
  254.     }
  255.     else
  256.     {
  257.         if (offset == 0) bg_scrolly_lsb = data;
  258.         if (offset == 1) bg_scrolly_msb = (data<<8);
  259.         twincobr_bgscroll_w(0,bg_scrolly_msb | bg_scrolly_lsb);
  260.     }
  261. }
  262. WRITE_HANDLER( wardner_fgscroll_w )
  263. {
  264.     if (offset & 2) {
  265.         if (offset == 2) fg_scrollx_lsb = data;
  266.         if (offset == 3) fg_scrollx_msb = (data<<8);
  267.         twincobr_fgscroll_w(2,fg_scrollx_msb | fg_scrollx_lsb);
  268.     }
  269.     else
  270.     {
  271.         if (offset == 0) fg_scrolly_lsb = data;
  272.         if (offset == 1) fg_scrolly_msb = (data<<8);
  273.         twincobr_fgscroll_w(0,fg_scrolly_msb | fg_scrolly_lsb);
  274.     }
  275. }
  276.  
  277. READ_HANDLER( wardner_videoram_r )
  278. {
  279.     int memdata = 0;
  280.     switch (offset) {
  281.         case 0: memdata =  twincobr_txram_r(0) & 0x00ff; break;
  282.         case 1: memdata = (twincobr_txram_r(0) & 0xff00) >> 8; break;
  283.         case 2: memdata =  twincobr_bgram_r(0) & 0x00ff; break;
  284.         case 3: memdata = (twincobr_bgram_r(0) & 0xff00) >> 8; break;
  285.         case 4: memdata =  twincobr_fgram_r(0) & 0x00ff; break;
  286.         case 5: memdata = (twincobr_fgram_r(0) & 0xff00) >> 8; break;
  287.     }
  288.     return memdata;
  289. }
  290.  
  291. WRITE_HANDLER( wardner_videoram_w )
  292. {
  293.     int memdata = 0;
  294.     switch (offset) {
  295.         case 0: memdata = twincobr_txram_r(0) & 0xff00;
  296.                 memdata |= data;
  297.                 twincobr_txram_w(0,memdata); break;
  298.         case 1: memdata = twincobr_txram_r(0) & 0x00ff;
  299.                 memdata |= (data << 8);
  300.                 twincobr_txram_w(0,memdata); break;
  301.         case 2: memdata = twincobr_bgram_r(0) & 0xff00;
  302.                 memdata |= data;
  303.                 twincobr_bgram_w(0,memdata); break;
  304.         case 3: memdata = twincobr_bgram_r(0) & 0x00ff;
  305.                 memdata |= (data << 8);
  306.                 twincobr_bgram_w(0,memdata); break;
  307.         case 4: memdata = twincobr_fgram_r(0) & 0xff00;
  308.                 memdata |= data;
  309.                 twincobr_fgram_w(0,memdata); break;
  310.         case 5: memdata = twincobr_fgram_r(0) & 0x00ff;
  311.                 memdata |= (data << 8);
  312.                 twincobr_fgram_w(0,memdata); break;
  313.     }
  314. }
  315.  
  316. static void twincobr_draw_sprites (struct osd_bitmap *bitmap, int priority)
  317. {
  318.     int offs;
  319.  
  320.     if (toaplan_main_cpu == 0) /* 68k */
  321.     {
  322.         for (offs = 0;offs < spriteram_size;offs += 8)
  323.         {
  324.             int attribute,sx,sy,flipx,flipy;
  325.             int sprite, color;
  326.  
  327.             attribute = READ_WORD(&buffered_spriteram[offs + 2]);
  328.             if ((attribute & 0x0c00) == priority) {    /* low priority */
  329.                 sy = READ_WORD(&buffered_spriteram[offs + 6]) >> 7;
  330.                 if (sy != 0x0100) {        /* sx = 0x01a0 or 0x0040*/
  331.                     sprite = READ_WORD(&buffered_spriteram[offs]) & 0x7ff;
  332.                     color  = attribute & 0x3f;
  333.                     sx = READ_WORD(&buffered_spriteram[offs + 4]) >> 7;
  334.                     flipx = attribute & 0x100;
  335.                     if (flipx) sx -= 14;        /* should really be 15 */
  336.                     flipy = attribute & 0x200;
  337.                     drawgfx(bitmap,Machine->gfx[3],
  338.                         sprite,
  339.                         color,
  340.                         flipx,flipy,
  341.                         sx-32,sy-16,
  342.                         &Machine->drv->visible_area,TRANSPARENCY_PEN,0);
  343.                 }
  344.             }
  345.         }
  346.     }
  347.     else /* Z80 */
  348.     {
  349.         for (offs = 0;offs < spriteram_size;offs += 8)
  350.         {
  351.             int attribute,sx,sy,flipx,flipy;
  352.             int sprite, color;
  353.  
  354.             attribute = READ_WORD_Z80(&buffered_spriteram[offs + 2]);
  355.             if ((attribute & 0x0c00) == priority) {    /* low priority */
  356.                 sy = READ_WORD_Z80(&buffered_spriteram[offs + 6]) >> 7;
  357.                 if (sy != 0x0100) {        /* sx = 0x01a0 or 0x0040*/
  358.                     sprite = READ_WORD_Z80(&buffered_spriteram[offs]) & 0x7ff;
  359.                     color  = attribute & 0x3f;
  360.                     sx = READ_WORD_Z80(&buffered_spriteram[offs + 4]) >> 7;
  361.                     flipx = attribute & 0x100;
  362.                     if (flipx) sx -= 14;        /* should really be 15 */
  363.                     flipy = attribute & 0x200;
  364.                     drawgfx(bitmap,Machine->gfx[3],
  365.                         sprite,
  366.                         color,
  367.                         flipx,flipy,
  368.                         sx-32,sy-16,
  369.                         &Machine->drv->visible_area,TRANSPARENCY_PEN,0);
  370.                 }
  371.             }
  372.         }
  373.     }
  374. }
  375.  
  376.  
  377.  
  378. void twincobr_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
  379. {
  380.   static int offs,code,tile,i,pal_base,sprite,color;
  381.   static int colmask[64];
  382.  
  383.  
  384.   if (twincobr_display_on) {
  385.     memset(palette_used_colors,PALETTE_COLOR_UNUSED,Machine->drv->total_colors * sizeof(unsigned char));
  386.     {
  387.     pal_base = Machine->drv->gfxdecodeinfo[2].color_codes_start;
  388.  
  389.     for (color = 0;color < 16;color++) colmask[color] = 0;
  390.  
  391.     for (offs = (twincobr_bgvideoram_size >> 1) - 2;offs >= 0;offs -= 2)
  392.     {
  393.         code  = READ_WORD(&twincobr_bgvideoram[(offs+twincobr_bg_ram_bank)]);
  394.         tile  = (code & 0x0fff);
  395.         color = (code & 0xf000) >> 12;
  396.         colmask[color] |= Machine->gfx[2]->pen_usage[tile];
  397.     }
  398.  
  399.     for (color = 0;color < 16;color++)
  400.     {
  401.         for (i = 0;i < 16;i++)
  402.         {
  403.             if (colmask[color] & (1 << i))
  404.                 palette_used_colors[pal_base + 16 * color + i] = PALETTE_COLOR_USED;
  405.         }
  406.     }
  407.  
  408.  
  409.     pal_base = Machine->drv->gfxdecodeinfo[1].color_codes_start;
  410.  
  411.     for (color = 0;color < 16;color++) colmask[color] = 0;
  412.  
  413.     scroll_x = (twincobr_flip_x_base + fgscrollx) & 0x01ff;
  414.     scroll_y = (twincobr_flip_y_base + fgscrolly) & 0x01ff;
  415.     vidbaseaddr = ((scroll_y>>3)*64) + (scroll_x>>3);
  416.     scroll_realign_x = scroll_x >> 3;
  417.     for (offs = (31*41)-1; offs >= 0; offs-- )
  418.     {
  419.         unsigned char sx,sy;
  420.         unsigned short int vidramaddr = 0;
  421.  
  422.         sx = offs % 41;
  423.         sy = offs / 41;
  424.         vidramaddr = ((vidbaseaddr + (sy*64) + sx) * 2);
  425.  
  426.         if ((scroll_realign_x + sx) > 63) vidramaddr -= 128;
  427.  
  428.         code  = READ_WORD(&twincobr_fgvideoram[(vidramaddr & 0x1fff)]);
  429.         tile  = (code & 0x0fff) | twincobr_fg_rom_bank;
  430.         color = (code & 0xf000) >> 12;
  431.         colmask[color] |= Machine->gfx[1]->pen_usage[tile];
  432.     }
  433.  
  434.     for (color = 0;color < 16;color++)
  435.     {
  436.         if (colmask[color] & (1 << 0))
  437.             palette_used_colors[pal_base + 16 * color] = PALETTE_COLOR_TRANSPARENT;
  438.         for (i = 1;i < 16;i++)
  439.         {
  440.             if (colmask[color] & (1 << i))
  441.                 palette_used_colors[pal_base + 16 * color + i] = PALETTE_COLOR_USED;
  442.         }
  443.     }
  444.  
  445.  
  446.     pal_base = Machine->drv->gfxdecodeinfo[3].color_codes_start;
  447.  
  448.     for (color = 0;color < 64;color++) colmask[color] = 0;
  449.  
  450.     if (toaplan_main_cpu == 0) /* 68k */
  451.     {
  452.         for (offs = 0;offs < spriteram_size;offs += 8)
  453.         {
  454.             int sy;
  455.             sy = READ_WORD(&buffered_spriteram[offs + 6]);
  456.             if (sy != 0x8000) {                    /* Is sprite is turned off ? */
  457.                 sprite = READ_WORD(&buffered_spriteram[offs]) & 0x7ff;
  458.                 color = READ_WORD(&buffered_spriteram[offs + 2]) & 0x3f;
  459.                 colmask[color] |= Machine->gfx[3]->pen_usage[sprite];
  460.             }
  461.         }
  462.     }
  463.     else /* Z80 */
  464.     {
  465.         for (offs = 0;offs < spriteram_size;offs += 8)
  466.         {
  467.             int sy;
  468.             sy = READ_WORD_Z80(&buffered_spriteram[offs + 6]);
  469.             if (sy != 0x8000) {                    /* Is sprite is turned off ? */
  470.                 sprite = READ_WORD_Z80(&buffered_spriteram[offs]) & 0x7ff;
  471.                 color = READ_WORD_Z80(&buffered_spriteram[offs + 2]) & 0x3f;
  472.                 colmask[color] |= Machine->gfx[3]->pen_usage[sprite];
  473.             }
  474.         }
  475.     }
  476.  
  477.     for (color = 0;color < 64;color++)
  478.     {
  479.         if (colmask[color] & (1 << 0))
  480.             palette_used_colors[pal_base + 16 * color] = PALETTE_COLOR_TRANSPARENT;
  481.         for (i = 1;i < 16;i++)
  482.         {
  483.             if (colmask[color] & (1 << i))
  484.                 palette_used_colors[pal_base + 16 * color + i] = PALETTE_COLOR_USED;
  485.         }
  486.     }
  487.  
  488.  
  489.     pal_base = Machine->drv->gfxdecodeinfo[0].color_codes_start;
  490.  
  491.     for (color = 0;color < 32;color++) colmask[color] = 0;
  492.  
  493.  
  494.     scroll_x = (twincobr_flip_x_base + txscrollx) & 0x01ff;
  495.     scroll_y = (twincobr_flip_y_base + txscrolly) & 0x00ff;
  496.     vidbaseaddr = ((scroll_y>>3)*64) + (scroll_x>>3);
  497.     scroll_realign_x = scroll_x>>3;
  498.     for (offs = (31*41)-1; offs >= 0; offs-- )
  499.     {
  500.         unsigned char sx,sy;
  501.         unsigned short int vidramaddr = 0;
  502.  
  503.         sx = offs % 41;
  504.         sy = offs / 41;
  505.  
  506.         vidramaddr = (vidbaseaddr + (sy*64) + sx) * 2;
  507.         if ((scroll_realign_x + sx) > 63) vidramaddr -= 128;
  508.         code = READ_WORD(&videoram[(vidramaddr & 0x0fff)]);
  509.         tile  = (code & 0x07ff);
  510.         color = (code & 0xf800) >> 11;
  511.         colmask[color] |= Machine->gfx[0]->pen_usage[tile];
  512.     }
  513.  
  514.  
  515.     for (color = 0;color < 32;color++)
  516.     {
  517.         if (colmask[color] & (1 << 0))
  518.             palette_used_colors[pal_base + 8 * color] = PALETTE_COLOR_TRANSPARENT;
  519.         for (i = 1;i < 8;i++)
  520.         {
  521.             if (colmask[color] & (1 << i))
  522.                 palette_used_colors[pal_base + 8 * color + i] = PALETTE_COLOR_USED;
  523.         }
  524.     }
  525.  
  526.  
  527.     if (palette_recalc())
  528.     {
  529.         memset(dirtybuffer,1,twincobr_bgvideoram_size >> 1);
  530.     }
  531.     }
  532.  
  533.  
  534.     /* draw the background */
  535.     for (offs = (twincobr_bgvideoram_size >> 1) - 2;offs >= 0;offs -= 2)
  536.     {
  537.         if (dirtybuffer[offs / 2])
  538.         {
  539.             int sx,sy;
  540.  
  541.             dirtybuffer[offs / 2] = 0;
  542.  
  543.             sx = (offs/2) % 64;
  544.             sy = (offs/2) / 64;
  545.  
  546.             code = READ_WORD(&twincobr_bgvideoram[offs+twincobr_bg_ram_bank]);
  547.             tile  = (code & 0x0fff);
  548.             color = (code & 0xf000) >> 12;
  549.             if (twincobr_flip_screen) { sx=63-sx; sy=63-sy; }
  550.             drawgfx(tmpbitmap,Machine->gfx[2],
  551.                 tile,
  552.                 color,
  553.                 twincobr_flip_screen,twincobr_flip_screen,
  554.                 8*sx,8*sy,
  555.                 0,TRANSPARENCY_NONE,0);
  556.         }
  557.     }
  558.  
  559.     /* copy the background graphics */
  560.     {
  561.         if (twincobr_flip_screen) {
  562.             scroll_x = (twincobr_flip_x_base + bgscrollx + 0x141) & 0x1ff;
  563.             scroll_y = (twincobr_flip_y_base + bgscrolly + 0xf1) & 0x1ff;
  564.         }
  565.         else {
  566.             scroll_x = (0x1c9 - bgscrollx) & 0x1ff;
  567.             scroll_y = (- 0x1e - bgscrolly) & 0x1ff;
  568.         }
  569.         copyscrollbitmap(bitmap,tmpbitmap,1,&scroll_x,1,&scroll_y,&Machine->drv->visible_area,TRANSPARENCY_NONE,0);
  570.     }
  571.  
  572.  
  573.     /* draw the sprites in low priority (Twin Cobra tanks under roofs) */
  574.     twincobr_draw_sprites (bitmap, 0x0400);
  575.  
  576.     /* draw the foreground */
  577.     scroll_x = (twincobr_flip_x_base + fgscrollx) & 0x01ff;
  578.     scroll_y = (twincobr_flip_y_base + fgscrolly) & 0x01ff;
  579.     vidbaseaddr = ((scroll_y>>3)*64) + (scroll_x>>3);
  580.     scroll_realign_x = scroll_x >> 3;        /* realign video ram pointer */
  581.     for (offs = (31*41)-1; offs >= 0; offs-- )
  582.     {
  583.         int xpos,ypos;
  584.         unsigned char sx,sy;
  585.         unsigned short int vidramaddr = 0;
  586.  
  587.         sx = offs % 41;
  588.         sy = offs / 41;
  589.  
  590.         vidramaddr = ((vidbaseaddr + (sy*64) + sx) * 2);
  591.         if ((scroll_realign_x + sx) > 63) vidramaddr -= 128;
  592.  
  593.         code  = READ_WORD(&twincobr_fgvideoram[(vidramaddr & 0x1fff)]);
  594.         tile  = (code & 0x0fff) | twincobr_fg_rom_bank;
  595.         color = (code & 0xf000) >> 12;
  596.         if (twincobr_flip_screen) { sx=40-sx; sy=30-sy; xpos=(sx*8) - (7-(scroll_x&7)); ypos=(sy*8) - (7-(scroll_y&7)); }
  597.         else { xpos=(sx*8) - (scroll_x&7); ypos=(sy*8) - (scroll_y&7); }
  598.         drawgfx(bitmap,Machine->gfx[1],
  599.             tile,
  600.             color,
  601.             twincobr_flip_screen,twincobr_flip_screen,
  602.             xpos,ypos,
  603.             &Machine->drv->visible_area,TRANSPARENCY_PEN,0);
  604.     }
  605.  
  606. /*********  Begin ugly sprite hack for Wardner when hero is in shop *********/
  607.     if ((wardner_sprite_hack) && (fgscrollx != bgscrollx)) {    /* Wardner ? */
  608.         if ((fgscrollx==0x1c9) || (twincobr_flip_screen && (fgscrollx==0x17a))) {    /* in the shop ? */
  609.             int wardner_hack = READ_WORD_Z80(&buffered_spriteram[0x0b04]);
  610.         /* sprite position 0x6300 to 0x8700 -- hero on shop keeper (normal) */
  611.         /* sprite position 0x3900 to 0x5e00 -- hero on shop keeper (flip) */
  612.             if ((wardner_hack > 0x3900) && (wardner_hack < 0x8700)) {    /* hero at shop keeper ? */
  613.                     wardner_hack = READ_WORD_Z80(&buffered_spriteram[0x0b02]);
  614.                     wardner_hack |= 0x0400;            /* make hero top priority */
  615.                     WRITE_WORD_Z80(&buffered_spriteram[0x0b02],wardner_hack);
  616.                     wardner_hack = READ_WORD_Z80(&buffered_spriteram[0x0b0a]);
  617.                     wardner_hack |= 0x0400;
  618.                     WRITE_WORD_Z80(&buffered_spriteram[0x0b0a],wardner_hack);
  619.                     wardner_hack = READ_WORD_Z80(&buffered_spriteram[0x0b12]);
  620.                     wardner_hack |= 0x0400;
  621.                     WRITE_WORD_Z80(&buffered_spriteram[0x0b12],wardner_hack);
  622.                     wardner_hack = READ_WORD_Z80(&buffered_spriteram[0x0b1a]);
  623.                     wardner_hack |= 0x0400;
  624.                     WRITE_WORD_Z80(&buffered_spriteram[0x0b1a],wardner_hack);
  625.             }
  626.         }
  627.     }
  628. /**********  End ugly sprite hack for Wardner when hero is in shop **********/
  629.  
  630.     /* draw the sprites in normal priority */
  631.     twincobr_draw_sprites (bitmap, 0x0800);
  632.  
  633.     /* draw the top layer */
  634.     scroll_x = (twincobr_flip_x_base + txscrollx) & 0x01ff;
  635.     scroll_y = (twincobr_flip_y_base + txscrolly) & 0x00ff;
  636.     vidbaseaddr = ((scroll_y>>3)*64) + (scroll_x>>3);
  637.     scroll_realign_x = scroll_x >> 3;
  638.     for (offs = (31*41)-1; offs >= 0; offs-- )
  639.     {
  640.         int xpos,ypos;
  641.         unsigned char sx,sy;
  642.         unsigned short int vidramaddr = 0;
  643.  
  644.         sx = offs % 41;
  645.         sy = offs / 41;
  646.  
  647.         vidramaddr = (vidbaseaddr + (sy*64) + sx) * 2;
  648.         if ((scroll_realign_x + sx) > 63) vidramaddr -=128;
  649.  
  650.         code  = READ_WORD(&videoram[(vidramaddr & 0x0fff)]);
  651.         tile  = (code & 0x07ff);
  652.         color = (code & 0xf800) >> 11;
  653.         if (twincobr_flip_screen) { sx=40-sx; sy=30-sy; xpos=(sx*8) - (7-(scroll_x&7)); ypos=(sy*8) - (7-(scroll_y&7)); }
  654.         else { xpos=(sx*8) - (scroll_x&7); ypos=(sy*8) - (scroll_y&7); }
  655.         drawgfx(bitmap,Machine->gfx[0],
  656.             tile,
  657.             color,
  658.             twincobr_flip_screen,twincobr_flip_screen,
  659.             xpos,ypos,
  660.             &Machine->drv->visible_area,TRANSPARENCY_PEN,0);
  661.     }
  662.  
  663.     /* draw the sprites in high priority */
  664.     twincobr_draw_sprites (bitmap, 0x0c00);
  665.  
  666.   }
  667. }
  668.  
  669. void twincobr_eof_callback(void)
  670. {
  671.     /*  Spriteram is always 1 frame ahead, suggesting spriteram buffering.
  672.         There are no CPU output registers that control this so we
  673.         assume it happens automatically every frame, at the end of vblank */
  674.     buffer_spriteram_w(0,0);
  675. }
  676.  
  677.